'''
Created on Dec 4, 2010

@author: Nican
'''

import math
import wx


class Gauge(wx.Window):
    def __init__(self, parent, name, color):
        sty = wx.NO_BORDER
        wx.Window.__init__(self, parent, style=sty)
        self.parent = parent
        self.SetBackgroundColour(wx.WHITE)
        self.SetCursor(wx.CROSS_CURSOR)
        
        self.SetMaxSize((100, 100))
        self.SetMinSize((100, 100))
        #self.SetBestFittingSize((200,200))
        #self.SetInitialSize((200,200))
        
        # Some initalisation, just to reminds the user that a variable
        # called self.BufferBmp exists. See self.OnSize().
        self.BufferBmp = None

        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        
        self.angle = 0
        self.name = name
        self.isOneLine = False
        
        text = wx.StaticText(self, -1, self.name, (0, 0), style=wx.ALIGN_CENTRE)
        text.SetForegroundColour( color )
    
    def SetAngle(self, angle):
        self.angle = math.radians(angle)
        #Refresh the screen to redraw
        if self.recalculateBuffer():
            self.Refresh()
    
    def oneLiner(self, state):
        self.isOneLine = state
        
    # OnSize is fired at the application start or when the frame is resized.
    # The OnSize event is fired BEFORE the OnPaint event. wxWindows
    # handles the events in this order. This is not due to the fact,
    # that the code line EVT_SIZE(...) is placed before the line
    # EVT_PAINT(...).
    # The procedure OnSize() is the right place to define the
    # BufferBmp and its size. self.BufferBmp is the picture in memory,
    # which contains your drawing. self.BufferBmp is also used as a flag,
    # a None value indicates no picture.
    #
    def OnSize(self, event):        
        if self.recalculateBuffer():
            self.Refresh()
            
    def recalculateBuffer(self):
        # Get the size of the drawing area in pixels.
        self.wi, self.he = self.GetSizeTuple()
        # Create BufferBmp and set the same size as the drawing area.
        self.BufferBmp = wx.EmptyBitmap(self.wi, self.he)
        memdc = wx.MemoryDC()
        memdc.SelectObject(self.BufferBmp)
        # Drawing job
        ret = self.DoSomeDrawing(memdc)
        if not ret:  #error
            self.BufferBmp = None
            
        return ret

    # OnPaint is executed at the app start, when resizing or when
    # the application windows becomes active. OnPaint copies the
    # buffered picture, instead of preparing (again) the drawing job.
    # This is the trick, copying is very fast.
    # Note: if you forget to define the dc in this procedure,
    # (no dc = ... code line), the application will run in
    # an infinite loop. This is a common beginner's error. (I think)
    # BeginDrawing() and EndDrawing() are for windows platforms (see doc).
    def OnPaint(self, event):
        dc = wx.PaintDC(self)
        
        dc.BeginDrawing()
        if self.BufferBmp != None:
            dc.DrawBitmap(self.BufferBmp, 0, 0, True)
        dc.EndDrawing()
    
    
    # The function defines the drawing job. Everything is drawn on the dc.
    # In that application, the dc corresponds to the BufferBmp.
    # Three things are drawn, a square with a fixed size, and two
    # rectangles with sizes determined by the size of the dc, that
    # means the size of the drawing area. Keep in mind, the size
    # of the drawing area depends on the size of the main frame,
    # which can be resized on the fly with your mouse.
    # At this point, I will introduce a small complication, that is
    # in fact quite practical. It may happen, the drawing is not
    # working correctly. Either there is an error in the drawing job
    # or the data you want to plot can not be drawn correctly. A typical
    # example is the plotting of 'scientific data'. The data are not (or
    # may not be) scaled correctly, that leads to errors, generally integer
    # overflow errors.
    # To circumvent this, the procedure returns True, if the drawing succeed.
    # It returns False, if the drawing fails. The returned value may be used
    # later.
    def DoSomeDrawing(self, dc):
        try:

            dc.BeginDrawing()
            
            dcwi, dche = dc.GetSizeTuple()

            #~ raise OverflowError #for test
            """
            # Clear everything
            dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
            dc.Clear()

            # Draw the square with a fixed size.
            dc.SetBrush(wx.Brush(wx.CYAN, wx.SOLID))
            dc.SetPen(wx.Pen(wx.BLUE, 1, wx.SOLID))
            dc.DrawRectangle(10, 10, 200, 200)

            # Draw a transparent rectangle with a red border, proportional to
            # the dc size.
            dcwi, dche = dc.GetSizeTuple()
            dc.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
            dc.SetPen(wx.Pen(wx.RED, 1, wx.SOLID))
            dc.DrawRectangle(0, 0, dcwi, dche)

            # Draw one another rectangle, a rectangle with a size proportional
            # to the dc size.
            gap = 50
            dc.SetBrush(wx.Brush(wx.WHITE, wx.TRANSPARENT))
            dc.SetPen(wx.Pen(wx.BLACK, 1, wx.SOLID))
            dc.DrawRectangle(0 + gap, 0 + gap, dcwi - 2 * gap, dche - 2 * gap)

            # These next 2 lines will raise an overflow error.
            #~ largeval = 1e10
            #~ dc.DrawLine(dcwi // 2, dche // 2, largeval, largeval)
            """
            dc.SetBrush(wx.Brush(wx.WHITE, wx.SOLID))
            dc.Clear()
            
            dc.DrawEllipticArc(0, 0, dcwi, dche, 0, 360)
            
            dc.DrawLine(
                        dcwi / 2,
                        dche / 2,
                        dcwi / 2 + math.cos(self.angle) * dcwi / 2,
                        dche / 2 - math.sin(self.angle) * dche / 2)
            
            if self.isOneLine == False:
                dc.DrawLine(
                        dcwi / 2,
                        dche / 2,
                        dcwi / 2 - math.cos(self.angle) * dcwi / 2,
                        dche / 2 + math.sin(self.angle) * dche / 2)

            dc.EndDrawing()
            return True

        except:
            return False
        
